require 'rextral.rb'

class Normalize < Transform
  class Splitter
    # This method is for the singletable dynamic horizontally split
    def self.method_missing(method_name)
      method_array = method_name.to_s.split("_")
      if method_array.include? "horizontally"
        $table_name = method_array[1..(method_array.index("horizontally")-1)].join("_")  
        $split_columnname = nil
        $split_rowposition = method_array.last
      end    
      return Splitter.new
    end



    # This method is for passing the connection attribute.
    # This is not ot be used in isolation.  
    def under connection
      if !$split_rowposition.nil?  
        table_name = Splitter.generate_missing_table_name($table_name,connection)
        primary_key = get_primary_key($table_name,connection)
        connection.execute("create table #{table_name} as (select * from #{$table_name} limit #{$split_rowposition.to_i})")
        if !primary_key.nil?
          connection.execute("ALTER TABLE #{table_name} ADD PRIMARY KEY AUTO_INCREMENT (#{primary_key})")
        end  
        puts "#{$table_name} was sucessfully splitted"
      end  
    end
    
    

    # This checks while creating the new table if there is a table of the same name
    # If the table is found then the tablename is changed.
    def self.generate_missing_table_name(table_name,connection)
      all_tables = connection.tables
      table_name = table_name+"_n"
      for table in all_tables
          table_name = table_name+"_n" if table.to_s == table_name
      end
      return table_name  
    end


    # This method does for a column what self.generate_missing_table_name does for a table.
    def self.generate_missing_column_name(column_name,table_name,connection)
      all_columns = get_column_list(connection,table_name)
      column_name = column_name+"_n"
      for column in all_columns
        column_name = column_name+"_n" if column.to_s == column_name
      end
      return column_name  
    end


    # This is for splitting different tables under different databases.
    def self.split_tables(options={})    
      split_obj = Splitter.new
      options.each{|key,value|
          connection = key
          value.each{|inner_key,inner_value|
            $table_name = inner_key
            $split_rowposition = inner_value
            split_obj.under connection
          }
      }    
    end
  end
  

  # This is the method missing. Here the dynamic transformer calls are captured and the string is parsed.
  # Based on the hash given transformations are carried out.
  def self.method_missing(method_name,child_tables)
    root_table = method_name.to_s.split("_")
    root_table_name = root_table[0..root_table.index("to")-1].join("_")
    primary_key_for_root = root_table[root_table.index("primary")+2..root_table.length-1].join("_")
    connection = child_tables[:connection]
    if handle_primary_key(primary_key_for_root,root_table_name,connection) != 0
      child_tables.each{|key,value|
      all_columns = connection.query("desc #{root_table_name}")
        if child_tables[:column_selection].downcase == "relative"
          Normalize.generate_column_name_hash(key.to_s,value,root_table_name,primary_key_for_root,connection,all_columns) if key.to_s != "connection" && key.to_s != "relative"
        elsif child_tables[:column_selection].downcase == "absolute"	
          normalize(key,value,connection,root_table_name,primary_key_for_root) if key.to_s != "connection" && key.to_s != "absolute"
        elsif child_tables[:column_selection].downcase == "numeric"
          Normalize.get_columns_from_number(key,value,root_table_name,primary_key_for_root,connection,all_columns)
        elsif child_tables[:column_selection].downcase == "hybrid"
          Normalize.hybrid_option_query_generator(key,value,root_table_name,primary_key_for_root,connection,all_columns)	
        end	
      }
    end	
  end



  # This method calls the normalize methods based on the format of giving columns.
  def self.hybrid_option_query_generator(key,value,root_table_name,primary_key_for_root,connection,all_columns)	
    if value.respond_to? :split
      Normalize.generate_column_name_hash(key,value,root_table_name,primary_key_for_root,connection,all_columns)
    elsif value.respond_to? :join
      if value[0].respond_to? :abs
        Normalize.get_columns_from_number(key,value,root_table_name,primary_key_for_root,connection,all_columns)
      elsif value[0].respond_to? :split
        Normalize.normalize(key,value,connection,root_table_name,primary_key_for_root)	
      end		
    end		
  end	



  # When the columns are mentioned as the numbers representing their positions from the root table.
  # this method will generate the column names for them and call the class level normalize method.
  def self.get_columns_from_number(tablename,number_arr,root_table,primary_key_for_root,connection,all_columns)
    if tablename.to_s != "column_selection" && tablename.to_s != "connection"
      column_names = []
      column_array = []
      column_counter = 1
      for column in all_columns
        if number_arr.include?column_counter
          column_names << column[0]
        end
        column_counter = column_counter + 1
      end
      Normalize.normalize("",tablename,column_names,connection,root_table,primary_key_for_root)
    end	
  end	


  
  # If the columns are mentioned as the relative positions of the root tables 
  # then this method will build an array of those columnnames and will pass them to the normalize method.	
  def self.generate_column_name_hash(key,value,root_table,primary_key_for_root,connection,all_columns)
    if key != "column_selection" && key != "connection"
      column_names = get_columns_from_positions(value,all_columns)
      Normalize.normalize("",key,column_names,connection,root_table,primary_key_for_root)
    end	
  end	



  # This method is actually building the normalization query
  def self.normalize(destination_connection,key,value,connection,root_table,primary_key_for_root)
    if key.to_s != "column_selection"
      column_names = []  
      if destination_connection != ""
        key = generate_missing_table_name(key,destination_connection)
      else
        key = generate_missing_table_name(key,connection)
      end    
        
      # Ensuring that root table has one or the other primary key
      primary_key = get_primary_key(root_table,connection)
      if !primary_key
        handle_primary_key(primary_key_for_root,root_table,connection)
      else
        primary_key_for_root = primary_key  
      end
        
      # Starting to build the query here.
      query_string = "CREATE TABLE "+key.to_s+" ("
      for single_value in value
        if single_value.respond_to?"join"
          single_value = single_value[0]
        end	  
        if single_value == primary_key_for_root || single_value == first_letter_capital(primary_key_for_root) || single_value == primary_key_for_root.capitalize || single_value == primary_key_for_root.gsub(/\_/,"") 
          foreign_key_already_added = true
        end		
        col_specs = get_the_col_spec_for_single_col(single_value.to_s,root_table,connection)
        query_string << single_value+" "+col_specs
        query_string << "," if value.size > 1
        column_names << single_value
      end
          
      # If the primary key is not added as a column,
      if !foreign_key_already_added
        foreign_key = root_table+"_"+primary_key_for_root
        col_specs = get_the_col_spec_for_single_col(primary_key_for_root,root_table,connection)
        query_string << "#{foreign_key} int(11) NOT NULL,"
      end	
        
      # Formatting the query to a currect SQL syntax.
      query_string[(query_string.length-1)] = ""
      query_string << ")"
      if destination_connection == ""
        connection.execute("#{query_string}")
        conection.execute("ALTER TABLE #{key.to_s} ADD FOREIGN KEY (#{root_table}_#{primary_key_for_root}) REFERENCES #{root_table}(#{primary_key_for_root})")
        execute_data_transfer_post_normalization(connection,key.to_s,connection,root_table,column_names,primary_key_for_root)
      else
        destination_connection.execute("#{query_string}")
        destination_connection.execute("ALTER TABLE #{key.to_s} ADD FOREIGN KEY (#{root_table}_#{primary_key_for_root}) REFERENCES #{root_table}(#{primary_key_for_root})")
        execute_data_transfer_post_normalization(destination_connection,key.to_s,connection,root_table,column_names,primary_key_for_root)
      end   
    end	
  end	    

end	

